#include "vm/swap.h"
#include <debug.h>
#include <inttypes.h>
#include <round.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <bitmap.h>

#include "userprog/gdt.h"
#include "userprog/pagedir.h"
#include "userprog/tss.h"
#include "userprog/syscall.h"
#include "filesys/directory.h"
#include "filesys/file.h"
#include "filesys/filesys.h"
#include "threads/flags.h"
#include "threads/init.h"
#include "threads/interrupt.h"
#include "threads/palloc.h"
#include "threads/thread.h"
#include "threads/synch.h"
#include "threads/vaddr.h"
#include "threads/malloc.h"
#include "threads/pte.h"

#include "vm/frame.h"
#include "vm/page.h"

void swap_init()
{
	swap_disk = disk_get(1,1);
	swap_n = disk_size(swap_disk)/FACTOR;
	swap_bitmap = bitmap_create (swap_n);
	lock_init(&swap_lock);
}

// try to find its position
int swap_position_find(void)
{
	unsigned int position = bitmap_scan_and_flip(swap_bitmap,0,1,false);
	if (position == BITMAP_ERROR)
		return -1;
	else
		return position;
}

// just writing a page from frame to disk only, nothing more than that!!
bool swap_out(struct page *_p)
{
	lock_acquire(&swap_lock);
	int pos = swap_position_find();
	if ( pos == -1)
	{
		lock_release(&swap_lock);
		return false;
	}
	void *kpage = pagedir_get_page(_p->t->pagedir, _p->upage);
	_p->swap_position = pos;

	int i;
	for ( i = 0 ; i < FACTOR ; ++i)
	{
		disk_write(swap_disk,pos*FACTOR+i,kpage+i*DISK_SECTOR_SIZE);
	}

	lock_release(&swap_lock);
	return true;
}

// just writing a page from disk to frame only!!
bool swap_in(struct page *_p)
{
	int pos = _p->swap_position;
	bool writable;
	void *kpage= frame_allocate(true);

	if (kpage==NULL)
	{
		return false;
	}	


	if( _p->isLoaded){

		uint32_t *pte;
		pte = lookup_page(_p->t->pagedir,_p->upage,false);

		// only one thing that I cannot sure!!
		// f___ing manual..
		writable = ((*pte & PTE_W)!=0 &&	(pte != NULL));

		lock_acquire(&swap_lock);

		bitmap_set(swap_bitmap,pos,false);
		int i ;	
		for ( i = 0 ; i < FACTOR ; ++i)
		{
			disk_read(swap_disk,pos*FACTOR+i,kpage+i*DISK_SECTOR_SIZE);
		}
		_p->swap_position = -1;

		lock_release(&swap_lock);

	}
	else{
		//		lock_acquire(&swap_lock);

		lock_acquire(&file_lock);

		int32_t off_t;

		off_t = file_tell(_p->fp);

		file_seek (_p->fp, _p->offset);

		if(_p->readbyte>0){
			if (file_read (_p->fp, kpage, _p->readbyte) != _p->readbyte)
			{
				//		palloc_free_page (kpage);
				file_seek(_p->fp, off_t);
				lock_release(&file_lock);

				struct thread *cur=thread_current();
				lock_acquire(&cur->page_lock);
				free(_p);
				lock_release(&cur->page_lock);

				frame_deallocate(kpage);
				return false; 
			}
		}

		memset (kpage + _p->readbyte, 0, PGSIZE - _p->readbyte );
		writable = _p->writable;
		_p->isLoaded = true;

		file_seek (_p->fp, off_t);

		lock_release(&file_lock);

		//		lock_release(&swap_lock);
	}


	// allocate frame at very last !!
	// if we didn't do that, then swap_in  and  page_insert(swap_in case) are in race condition

	if ( !frame_insert_page(_p->upage,kpage, writable) )
	{
		frame_deallocate(kpage);
		return false;
	}

	return true;
}

// remove a page from disk (but not by swap in call)
void swap_remove_page(struct page *_p)
{
	int temp = _p->swap_position;
	if( temp == -1 )
		return;

	_p->swap_position = -1;	

	lock_acquire(&swap_lock);
	bitmap_set(swap_bitmap, temp ,false);
	lock_release(&swap_lock);
}
